package io.gitee.zhangbinhub.admin.log.schedule

import io.gitee.zhangbinhub.acp.boot.base.BaseSpringBootScheduledAsyncTask
import io.gitee.zhangbinhub.acp.boot.exceptions.WebException
import io.gitee.zhangbinhub.acp.boot.log.LogAdapter
import io.gitee.zhangbinhub.acp.cloud.tools.CloudTools
import io.gitee.zhangbinhub.acp.cloud.lock.DistributedLock
import io.gitee.zhangbinhub.acp.core.common.CalendarTools
import io.gitee.zhangbinhub.acp.core.common.CommonTools
import io.gitee.zhangbinhub.admin.log.conf.LogServerCustomerConfiguration
import io.gitee.zhangbinhub.admin.log.constant.LogBackUp
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import java.io.File

/**
 * @since JDK 11
 */
@Component("LogFileBackUpTask")
class LogFileBackUpTask @Autowired
constructor(
    private val logAdapter: LogAdapter,
    private val cloudTools: CloudTools,
    private val logServerCustomerConfiguration: LogServerCustomerConfiguration,
    private val distributedLock: DistributedLock,
) : BaseSpringBootScheduledAsyncTask() {
    private val lockName = "back_up"

    init {
        taskName = "日志文件备份任务"
    }

    override fun beforeExecuteFun(): Boolean =
        distributedLock.getLock(
            "${LogBackUp.LOG_BACKUP_DISTRIBUTED_LOCK_KEY}_$lockName" + "_" + cloudTools.getServerIp() + ":" + cloudTools.getServerPort(),
            LogBackUp.LOG_BACKUP_DISTRIBUTED_LOCK_TIME_OUT
        )

    override fun executeFun(): Any? {
        logAdapter.info("开始执行日志文件备份")
        try {
            var day = CalendarTools.getPrevDay(CalendarTools.getNowDateTime())
            val dayNumber = if (logServerCustomerConfiguration.maxHistoryDayNumber > 0) {
                logServerCustomerConfiguration.maxHistoryDayNumber
            } else {
                1
            }
            (0 until dayNumber).forEach { i ->
                val logFileDate = CalendarTools.getDateTimeString(day, CalendarTools.DATE_FORMAT)
                val logFold = File(logServerCustomerConfiguration.logFilePath)
                val logFoldPath = logFold.canonicalPath
                var zipFilePath = logFoldPath + LogBackUp.BACK_UP_PATH + File.separator +
                        LogBackUp.ZIP_FILE_PREFIX + logFileDate + "_" + cloudTools.getServerIp() + "_" + cloudTools.getServerPort() + LogBackUp.EXTENSION
                val zipFile = File(zipFilePath)
                if (!zipFile.exists()) {
                    if (!logFold.exists() || !logFold.isDirectory) {
                        throw WebException("路径 $logFoldPath 不存在或不是文件夹")
                    }
                    val files = logFold.listFiles { pathname ->
                        pathname.name.contains(logFileDate) && !pathname.name.endsWith(
                            ".tmp",
                            true
                        )
                    }
                    if (files != null && files.isNotEmpty()) {
                        logAdapter.info(logFoldPath + " 路径下 " + logFileDate + " 的日志文件（或文件夹）共 " + files.size + " 个")
                        val fileNames: MutableList<String> = mutableListOf()
                        for (file in files) {
                            fileNames.add(file.canonicalPath)
                        }
                        logAdapter.info("开始执行文件压缩...")
                        zipFilePath = CommonTools.filesToZip(fileNames, zipFilePath, true)
                        if (!CommonTools.isNullStr(zipFilePath)) {
                            logAdapter.info("文件压缩完成，压缩文件为：$zipFilePath")
                        } else {
                            logAdapter.info("文件压缩失败！")
                        }
                    } else {
                        logAdapter.debug("$logFoldPath 路径下没有 $logFileDate 的日志文件")
                    }
                }
                day = CalendarTools.getPrevDay(day)
            }
        } catch (e: Exception) {
            logAdapter.error(e.message, e)
        }

        return true
    }

    override fun afterExecuteFun(result: Any) {
        try {
            if (logServerCustomerConfiguration.maxHistoryDayNumber > 0)
                doClearBackUpFiles()
        } catch (e: Exception) {
            logAdapter.error(e.message, e)
        } finally {
            distributedLock.releaseLock(
                "${LogBackUp.LOG_BACKUP_DISTRIBUTED_LOCK_KEY}_$lockName" + "_" + cloudTools.getServerIp() + ":" + cloudTools.getServerPort()
            )
        }
    }

    private fun doClearBackUpFiles() {
        logAdapter.info("开始清理历史备份文件，最大保留天数：" + logServerCustomerConfiguration.maxHistoryDayNumber)
        val filterFileNameRegex: MutableSet<String> = mutableSetOf()
        filterFileNameRegex.add(
            logServerCustomerConfiguration.logFile
                .substring(logServerCustomerConfiguration.logFilePath.length + 1)
                .replace(".", "\\.")
        )
        filterFileNameRegex.add(LogBackUp.BACK_UP_PATH.substring(1))
        val filterZipFileNameRegex: MutableSet<String> = mutableSetOf()
        // 保留当天和历史最大天数的文件
        var day = CalendarTools.getNowDateTime()
        (0..logServerCustomerConfiguration.maxHistoryDayNumber).forEach { i ->
            filterFileNameRegex.add(CalendarTools.getDateTimeString(day, CalendarTools.DATE_FORMAT))
            filterZipFileNameRegex.add(
                LogBackUp.ZIP_FILE_PREFIX + CalendarTools.getDateTimeString(
                    day,
                    CalendarTools.DATE_FORMAT
                ) + ".*" + LogBackUp.EXTENSION.replace(".", "\\.")
            )
            day = CalendarTools.getPrevDay(day)
        }
        // 清理历史日志文件
        val fold = File(logServerCustomerConfiguration.logFilePath)
        doDeleteFileForFold(fold, filterFileNameRegex)
        // 清理历史备份压缩日志文件
        val backUpFold = File(logServerCustomerConfiguration.logFilePath + LogBackUp.BACK_UP_PATH)
        doDeleteFileForFold(backUpFold, filterZipFileNameRegex)
        logAdapter.info("清理历史备份文件完成！")
    }

    private fun doDeleteFileForFold(fold: File, filterNameRegex: Set<String>) {
        if (fold.exists()) {
            fold.listFiles { file ->
                var delete = true
                for (regex in filterNameRegex) {
                    if (CommonTools.regexPattern(regex, file.name)) {
                        delete = false
                        break
                    }
                }
                delete
            }?.forEach { file ->
                CommonTools.doDeleteFile(file, false)
            }
        }
    }
}
